#ifndef _RTJOB_H_
#define _RTJOB_H_

#include "RtObject.h"

/** Base class for objects that perform a specific task reqursively.

Jobs can be organized as children of an RtLoop, which encapsulates a software loop
that executes all child-jobs in each repetition.

In order for the job to perform its task it must also be armed
by calling arm(). Arming does all the necessary initialization in the object.

If the slave property is true, then the job is automatically armed by
its parent job.

\ingroup RtCore
*/
class RTLAB_BASE_EXPORT RtJob : public RtObject
{
	Q_OBJECT
	/** True if job is armed.
	An armed job is ready to run. If the RtJob belongs to a loop
	it will execute its task.
	Setting the property true will arm the job and all child-jobs.
	*/
	Q_PROPERTY(bool armed READ armed WRITE setArmed)
	/** True if this job is a slave.
	An slave job is automatically armed by its parent.
	*/
	Q_PROPERTY(bool slave READ slave WRITE setSlave)

protected:
    bool armed_, slave_;

	/** Performs internal initialization for the job.
	*/
	virtual bool arm_() { armed_ = true; return armed_; }
	/// Disarms the job and performs internal de-initialization.
	virtual void disarm_() { armed_ = false;}

public:
	bool armed() { return armed_; }
	bool setArmed(bool on);
	bool slave() { return slave_; }
    bool setSlave(bool on);

protected:

	friend class JobList;
	class JobList : public QList<RtJob*>
	{
		public:
			void exec() { foreach(RtJob* job, *this) job->exec();  } 
            void lock() { for(iterator i=begin(); i<end(); ++i) (*i)->jobLock(); }
            void unlock() { for(iterator i=end()-1; i>=begin(); --i) (*i)->jobUnlock(); }
	};

	friend class JobLocker;
	class JobLocker
	{
		RtJob* j;
	public:
        JobLocker(RtJob* aj) : j(aj) { j->jobLock(); }
        ~JobLocker()  { j->jobUnlock(); }
	};

    /** A list of jobs that are children of this job.
      The list is populated when the job is armed.
      During task execution, the run() function of all
      jobs in the list is called.
      */
    JobList subjobs_;

    void jobLock() { comm_lock.lock(); subjobs_.lock(); }
    void jobUnlock() { subjobs_.unlock(); comm_lock.unlock(); }

protected:
	virtual void exec();
	virtual void run() { updateWidgets(); }

	bool throwIfArmed();

public:
	/** Constructor of RtJob objects.
	If parent is also a RtJob then the currently created RtJob is added to the
	list of this parent's child-jobs.
	*/
	Q_INVOKABLE RtJob(const QString& name);
	virtual ~RtJob(void);

	virtual void detach();

public slots:
    /** Arm the job.
      */
	bool arm() { return setArmed(true); }
	/// Disarm the job & child-jobs.
	void disarm() { setArmed(false); }
};

/** Base class of all RtLab loops.

\ingroup RtCore

RtLoop encapsulates a loop that is executed reqursively.
*/
class RTLAB_BASE_EXPORT RtLoop : public RtJob
{
protected:
    virtual void exec();
public:
    RtLoop(const QString& name);
    virtual ~RtLoop(void);
};

/** A loop that is executed with a specified delay.

\ingroup RtCore

RtDelayLoop is a loop that is executed once every delay cycles.

The number of delay cycles is a property of RtDelayLoop and can be set by script code.
*/
class RTLAB_BASE_EXPORT RtDelayLoop : public RtLoop
{
    Q_OBJECT
    /** Number of parent loop cycles before this loop is called.
    The value of 0 is allowed.
    */
    Q_PROPERTY(uint delay READ delay WRITE setDelay)
protected:
    unsigned int delay_, counter_;
    virtual void exec();

public:
	Q_INVOKABLE RtDelayLoop(const QString& name);
    virtual ~RtDelayLoop(void);

    unsigned int delay() const { return delay_; }
    void setDelay(unsigned int d);
};



#endif


 
